snapshot: Work on pushing and popping again
authorBenjamin Otte <otte@redhat.com>
Tue, 13 Dec 2016 08:40:24 +0000 (09:40 +0100)
committerBenjamin Otte <otte@redhat.com>
Tue, 20 Dec 2016 17:01:10 +0000 (18:01 +0100)
It is now possible to call push() subfunctions for simple container
nodes with just a single child. So you can for example
gtk_snapshot_push_clip() a clip region that all the nodes that get
appended later will then obey.

gtk_snapshot_pop() will then not return a container node, but a clip
node containing the container node (and similar for the transform
example).

This is implemented internally by providing a "collect function" when
pushing that is called when popping to collects all the accumulated
nodes and combine them into the single node that gets returned.

To simplify things even more, gtk_snapshot_pop_and_append() has been
added, which pops the currently pushed node and appends it to the
parent.

The icon rendering code has been converted to this approach.

docs/reference/gtk/gtk4-sections.txt
gtk/gtkrendericon.c
gtk/gtksnapshot.c
gtk/gtksnapshot.h
gtk/gtksnapshotprivate.h

index 8e1f9e6d8c5d26741c0566ce45a4765e3aaaa7c5..bab84761be9dea87871e67749e9ec28025f697b9 100644 (file)
@@ -4455,7 +4455,10 @@ gtk_volume_button_get_type
 GtkSnapshot
 gtk_snapshot_push
 gtk_snapshot_push_node
+gtk_snapshot_push_transform
+gtk_snapshot_push_clip
 gtk_snapshot_pop
+gtk_snapshot_pop_and_append
 gtk_snapshot_set_transform
 gtk_snapshot_transform
 gtk_snapshot_translate_2d
index 9be7201e83a7c5db4b2bb72ee61056216e0a4573..3af2613b76dc57a097f847ecb2ce5eff3fcf3be4 100644 (file)
@@ -126,26 +126,18 @@ gtk_css_style_snapshot_icon (GtkCssStyle            *style,
   else
     {
       graphene_matrix_t m1, m2, m3;
-      GskRenderNode *transform_node, *icon_node;
-      double offset_x, offset_y;
 
-      gtk_snapshot_get_offset (snapshot, &offset_x, &offset_y);
       /* XXX: Implement -gtk-icon-transform-origin instead of hardcoding "50% 50%" here */
-      graphene_matrix_init_translate (&m1, &GRAPHENE_POINT3D_INIT(offset_x + width / 2.0, offset_y + height / 2.0, 0));
+      graphene_matrix_init_translate (&m1, &GRAPHENE_POINT3D_INIT (width / 2.0, height / 2.0, 0));
       graphene_matrix_multiply (&transform_matrix, &m1, &m3);
-      graphene_matrix_init_translate (&m2, &GRAPHENE_POINT3D_INIT(- width / 2.0, - height / 2.0, 0));
+      graphene_matrix_init_translate (&m2, &GRAPHENE_POINT3D_INIT (- width / 2.0, - height / 2.0, 0));
       graphene_matrix_multiply (&m2, &m3, &m1);
 
-      gtk_snapshot_push (snapshot, FALSE, "CSS Icon Transform Container");
+      gtk_snapshot_push_transform (snapshot, &m1, "CSS Icon Transform Container");
+
       gtk_css_image_builtin_snapshot (image, snapshot, width, height, builtin_type);
-      icon_node = gtk_snapshot_pop (snapshot);
-
-      transform_node = gsk_transform_node_new (icon_node, &m1);
-      gsk_render_node_set_name (transform_node, "CSS Icon Transform");
-      gtk_snapshot_append_node (snapshot, transform_node);
-      
-      gsk_render_node_unref (transform_node);
-      gsk_render_node_unref (icon_node);
+
+      gtk_snapshot_pop_and_append (snapshot);
     }
 }
 
@@ -272,7 +264,6 @@ gtk_css_style_snapshot_icon_texture (GtkCssStyle *style,
   const GtkCssValue *shadows, *transform;
   graphene_matrix_t transform_matrix;
   graphene_rect_t bounds;
-  GskRenderNode *icon_node, *transform_node;
   double width, height;
   static gboolean shadow_warning;
 
@@ -306,24 +297,18 @@ gtk_css_style_snapshot_icon_texture (GtkCssStyle *style,
   else
     {
       graphene_matrix_t translate, matrix;
-      double offset_x, offset_y;
 
-      gtk_snapshot_get_offset (snapshot, &offset_x, &offset_y);
       /* XXX: Implement -gtk-icon-transform-origin instead of hardcoding "50% 50%" here */
-      graphene_matrix_init_translate (&translate, &GRAPHENE_POINT3D_INIT(offset_x + width / 2.0, offset_y + height / 2.0, 0));
+      graphene_matrix_init_translate (&translate, &GRAPHENE_POINT3D_INIT (width / 2.0, height / 2.0, 0));
       graphene_matrix_multiply (&transform_matrix, &translate, &matrix);
       graphene_matrix_translate (&matrix, &GRAPHENE_POINT3D_INIT(- width / 2.0, - height / 2.0, 0));
       graphene_matrix_scale (&matrix, 1.0 / texture_scale, 1.0 / texture_scale, 1);
 
-      graphene_rect_init (&bounds, 0, 0, gsk_texture_get_width (texture), gsk_texture_get_height (texture));
-      icon_node = gsk_texture_node_new (texture, &bounds);
-      gsk_render_node_set_name (icon_node, "Icon");
+      gtk_snapshot_push_transform (snapshot, &matrix, "Icon Transform");
 
-      transform_node = gsk_transform_node_new (icon_node, &matrix);
-      gsk_render_node_set_name (transform_node, "Icon Transform");
-      gtk_snapshot_append_node (snapshot, transform_node);
+      graphene_rect_init (&bounds, 0, 0, gsk_texture_get_width (texture), gsk_texture_get_height (texture));
+      gtk_snapshot_append_texture_node (snapshot, texture, &bounds, "Icon");
 
-      gsk_render_node_unref (icon_node);
-      gsk_render_node_unref (transform_node);
+      gtk_snapshot_pop_and_append (snapshot);
     }
 }
index 9ba07a5c4c55f1830d90add2200b6ef3245e4860..663fd582c3ea2132ce44cb0140e86cba52b2314b 100644 (file)
  * the #GtkWidget::snapshot vfunc.
  */
 
+static GskRenderNode *
+gtk_snapshot_collect_default (GskRenderNode **nodes,
+                              guint           n_nodes,
+                              const char     *name,
+                              gpointer        unused)
+{
+  GskRenderNode *node;
+
+  if (n_nodes == 0)
+    {
+      node = NULL;
+    }
+  else if (n_nodes == 1)
+    {
+      node = gsk_render_node_ref (nodes[0]);
+    }
+  else
+    {
+      node = gsk_container_node_new (nodes, n_nodes);
+      gsk_render_node_set_name (node, name);
+    }
+
+  return node;
+}
+
 static GtkSnapshotState *
-gtk_snapshot_state_new (GtkSnapshotState *parent,
-                        char             *name,
-                        cairo_region_t   *clip,
-                        double            translate_x,
-                        double            translate_y)
+gtk_snapshot_state_new (GtkSnapshotState       *parent,
+                        char                   *name,
+                        cairo_region_t         *clip,
+                        double                  translate_x,
+                        double                  translate_y,
+                        GtkSnapshotCollectFunc  collect_func,
+                        gpointer                collect_data)
 {
   GtkSnapshotState *state;
 
@@ -63,10 +90,12 @@ gtk_snapshot_state_new (GtkSnapshotState *parent,
 
   state->parent = parent;
   state->name = name;
-  state->translate_x = translate_x;
-  state->translate_y = translate_y;
   if (clip)
     state->clip_region = cairo_region_reference (clip);
+  state->translate_x = translate_x;
+  state->translate_y = translate_y;
+  state->collect_func = collect_func;
+  state->collect_data = collect_data;
 
   return state;
 }
@@ -110,7 +139,9 @@ gtk_snapshot_init (GtkSnapshot          *snapshot,
   snapshot->state = gtk_snapshot_state_new (NULL,
                                             str,
                                             (cairo_region_t *) clip,
-                                            0, 0);
+                                            0, 0,
+                                            gtk_snapshot_collect_default,
+                                            NULL);
 }
 
 GskRenderNode *
@@ -168,17 +199,159 @@ gtk_snapshot_push (GtkSnapshot           *snapshot,
                                                 str,
                                                 snapshot->state->clip_region,
                                                 snapshot->state->translate_x,
-                                                snapshot->state->translate_y);
+                                                snapshot->state->translate_y,
+                                                gtk_snapshot_collect_default,
+                                                NULL);
     }
   else
     {
       snapshot->state = gtk_snapshot_state_new (snapshot->state,
                                                 str,
                                                 NULL,
-                                                0, 0);
+                                                0, 0,
+                                                gtk_snapshot_collect_default,
+                                                NULL);
     }
 }
 
+static GskRenderNode *
+gtk_snapshot_collect_transform (GskRenderNode **nodes,
+                                guint           n_nodes,
+                                const char     *name,
+                                gpointer        transform)
+{
+  GskRenderNode *node, *transform_node;
+
+  node = gtk_snapshot_collect_default (nodes, n_nodes, name, NULL);
+  if (node == NULL)
+    return NULL;
+
+  transform_node = gsk_transform_node_new (node, transform);
+  gsk_render_node_set_name (transform_node, name);
+
+  gsk_render_node_unref (node);
+  g_slice_free (graphene_matrix_t, transform);
+
+  return transform_node;
+}
+
+void
+gtk_snapshot_push_transform (GtkSnapshot             *snapshot,
+                             const graphene_matrix_t *transform,
+                             const char              *name,
+                             ...)
+{
+  graphene_matrix_t offset;
+  graphene_matrix_t* real_transform;
+
+  graphene_matrix_init_translate (&offset,
+                                  &GRAPHENE_POINT3D_INIT(
+                                      snapshot->state->translate_x,
+                                      snapshot->state->translate_y,
+                                      0
+                                  ));
+
+  real_transform = g_slice_new (graphene_matrix_t);
+  graphene_matrix_multiply (transform, &offset, real_transform);
+
+  char *str;
+
+  if (name)
+    {
+      va_list args;
+
+      va_start (args, name);
+      str = g_strdup_vprintf (name, args);
+      va_end (args);
+    }
+  else
+    str = NULL;
+
+  snapshot->state = gtk_snapshot_state_new (snapshot->state,
+                                            str,
+                                            NULL,
+                                            0, 0,
+                                            gtk_snapshot_collect_transform,
+                                            real_transform);
+}
+
+static void
+rectangle_init_from_graphene (cairo_rectangle_int_t *cairo,
+                              const graphene_rect_t *graphene)
+{
+  cairo->x = floorf (graphene->origin.x);
+  cairo->y = floorf (graphene->origin.y);
+  cairo->width = ceilf (graphene->origin.x + graphene->size.width) - cairo->x;
+  cairo->height = ceilf (graphene->origin.y + graphene->size.height) - cairo->y;
+}
+
+static GskRenderNode *
+gtk_snapshot_collect_clip (GskRenderNode **nodes,
+                           guint           n_nodes,
+                           const char     *name,
+                           gpointer        bounds)
+{
+  GskRenderNode *node, *clip_node;
+
+  node = gtk_snapshot_collect_default (nodes, n_nodes, name, NULL);
+  if (node == NULL)
+    return NULL;
+
+  clip_node = gsk_clip_node_new (node, bounds);
+  gsk_render_node_set_name (clip_node, name);
+
+  gsk_render_node_unref (node);
+  g_slice_free (graphene_rect_t, bounds);
+
+  return clip_node;
+}
+
+void
+gtk_snapshot_push_clip (GtkSnapshot           *snapshot,
+                        const graphene_rect_t *bounds,
+                        const char            *name,
+                        ...)
+{
+  graphene_rect_t *real_bounds;
+  cairo_region_t *clip;
+  cairo_rectangle_int_t rect;
+  char *str;
+
+  real_bounds = g_slice_new (graphene_rect_t);
+  graphene_rect_offset_r (bounds, snapshot->state->translate_x, snapshot->state->translate_y, real_bounds);
+
+  if (name)
+    {
+      va_list args;
+
+      va_start (args, name);
+      str = g_strdup_vprintf (name, args);
+      va_end (args);
+    }
+  else
+    str = NULL;
+  
+  rectangle_init_from_graphene (&rect, real_bounds);
+  if (snapshot->state->clip_region)
+    {
+      clip = cairo_region_copy (snapshot->state->clip_region);
+      cairo_region_intersect_rectangle (clip, &rect);
+    }
+  else
+    {
+      clip = cairo_region_create_rectangle (&rect);
+    }
+  snapshot->state = gtk_snapshot_state_new (snapshot->state,
+                                            str,
+                                            clip,
+                                            snapshot->state->translate_x,
+                                            snapshot->state->translate_y,
+                                            gtk_snapshot_collect_clip,
+                                            real_bounds);
+
+  cairo_region_destroy (clip);
+}
+
 /**
  * gtk_snapshot_pop:
  * @snapshot: a #GtkSnapshot
@@ -207,26 +380,35 @@ gtk_snapshot_pop (GtkSnapshot *snapshot)
   state = snapshot->state;
   snapshot->state = state->parent;
 
-  if (state->nodes->len == 0)
-    {
-      node = NULL;
-    }
-  else if (state->nodes->len == 1)
-    {
-      node = gsk_render_node_ref (g_ptr_array_index (state->nodes, 0));
-    }
-  else
-    {
-      node = gsk_container_node_new ((GskRenderNode **) state->nodes->pdata,
-                                     state->nodes->len);
-      gsk_render_node_set_name (node, state->name);
-    }
+  node = state->collect_func ((GskRenderNode **) state->nodes->pdata,
+                              state->nodes->len,
+                              state->name,
+                              state->collect_data);
 
   gtk_snapshot_state_free (state);
 
   return node;
 }
 
+/**
+ * gtk_snapshot_pop_and_append:
+ * @snapshot: a #GtkSnapshot
+ *
+ * Removes the top element from the stack of render nodes,
+ * and appends it to the node underneath it.
+ *
+ * Since: 3.90
+ */
+void
+gtk_snapshot_pop_and_append (GtkSnapshot *snapshot)
+{
+  GskRenderNode *node;
+
+  node = gtk_snapshot_pop (snapshot);
+  gtk_snapshot_append_node (snapshot, node);
+  gsk_render_node_unref (node);
+}
+
 /**
  * gtk_snapshot_get_renderer:
  * @snapshot: a #GtkSnapshot
@@ -468,16 +650,6 @@ gtk_snapshot_append_color_node (GtkSnapshot           *snapshot,
   gsk_render_node_unref (node);
 }
 
-static void
-rectangle_init_from_graphene (cairo_rectangle_int_t *cairo,
-                              const graphene_rect_t *graphene)
-{
-  cairo->x = floorf (graphene->origin.x);
-  cairo->y = floorf (graphene->origin.y);
-  cairo->width = ceilf (graphene->origin.x + graphene->size.width) - cairo->x;
-  cairo->height = ceilf (graphene->origin.y + graphene->size.height) - cairo->y;
-}
-
 /**
  * gtk_snapshot_clips_rect:
  * @snapshot: a #GtkSnapshot
index 7b7e5efe0925fafc2ddbb928a091efeaab9e1ec2..5f4bb4b4746daa7bb8ef8d0682aff7e1542c452b 100644 (file)
@@ -42,7 +42,19 @@ void            gtk_snapshot_push                       (GtkSnapshot
                                                          const char             *name,
                                                          ...) G_GNUC_PRINTF (3, 4);
 GDK_AVAILABLE_IN_3_90
+void            gtk_snapshot_push_transform             (GtkSnapshot            *snapshot,
+                                                         const graphene_matrix_t*transform,
+                                                         const char             *name,
+                                                         ...) G_GNUC_PRINTF (3, 4);
+GDK_AVAILABLE_IN_3_90
+void            gtk_snapshot_push_clip                  (GtkSnapshot            *snapshot,
+                                                         const graphene_rect_t  *bounds,
+                                                         const char             *name,
+                                                         ...) G_GNUC_PRINTF (3, 4);
+GDK_AVAILABLE_IN_3_90
 GskRenderNode * gtk_snapshot_pop                        (GtkSnapshot            *snapshot) G_GNUC_WARN_UNUSED_RESULT;
+GDK_AVAILABLE_IN_3_90
+void            gtk_snapshot_pop_and_append             (GtkSnapshot            *snapshot);
 
 GDK_AVAILABLE_IN_3_90
 void            gtk_snapshot_translate_2d               (GtkSnapshot            *snapshot,
index 9050c4f652be418b0667c81cfbc14ffc6a3d0bb4..7eba0f48cc4413713b709a4ab08efd2ee8ceddd8 100644 (file)
@@ -24,6 +24,11 @@ G_BEGIN_DECLS
 
 typedef struct _GtkSnapshotState GtkSnapshotState;
 
+typedef GskRenderNode * (* GtkSnapshotCollectFunc) (GskRenderNode **nodes,
+                                                    guint           n_nodes,
+                                                    const char     *name,
+                                                    gpointer        user_data);
+
 struct _GtkSnapshotState {
   GtkSnapshotState      *parent;
 
@@ -33,6 +38,9 @@ struct _GtkSnapshotState {
   cairo_region_t        *clip_region;
   double                 translate_x;
   double                 translate_y;
+
+  GtkSnapshotCollectFunc collect_func;
+  gpointer               collect_data;
 };
 
 struct _GtkSnapshot {